﻿using System;
using System.IO;
using CuteAnt.IO;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using Overwrite = ICSharpCode.SharpZipLib.Zip.FastZip.Overwrite;

namespace CuteAnt.Zip
{
	public class ZipICSharp
	{
		#region -- method ZipPackFile --

		public static void ZipPackFile(String zipedFile, String fileToZip)
		{
			ZipPackFile(zipedFile, fileToZip, 6, null, null);
		}

		public static void ZipPackFile(String zipedFile, String fileToZip, Int32 compressionLevel)
		{
			ZipPackFile(zipedFile, fileToZip, compressionLevel, null, null);
		}

		public static void ZipPackFile(String zipedFile, String fileToZip, String password)
		{
			ZipPackFile(zipedFile, fileToZip, 6, password, null);
		}

		public static void ZipPackFile(String zipedFile, String fileToZip, Int32 compressionLevel, String password, String comment)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipedFile, "zipedFile");
			ValidationHelper.ArgumentNullOrEmpty(fileToZip, "fileToZip");
			fileToZip = FileHelper.FileExists(fileToZip);
			ValidationHelper.ArgumentCondition(fileToZip.IsNullOrWhiteSpace(), "fileToZip", "Argument '{0}' 文件不存在!");

			String[] files = { fileToZip };
			ZipPackFiles(zipedFile, files, compressionLevel, password, comment);
		}

		public static void ZipPackFiles(String zipedFile, String[] filesToZip)
		{
			ZipPackFiles(zipedFile, filesToZip, 6, null, null);
		}

		public static void ZipPackFiles(String zipedFile, String[] filesToZip, Int32 compressionLevel)
		{
			ZipPackFiles(zipedFile, filesToZip, 6, null, null);
		}

		public static void ZipPackFiles(String zipedFile, String[] filesToZip, String password)
		{
			ZipPackFiles(zipedFile, filesToZip, 6, null, null);
		}

		public static void ZipPackFiles(String zipedFile, String[] filesToZip, Int32 compressionLevel, String password, String comment)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipedFile, "zipedFile");
			ValidationHelper.ArgumentNull(filesToZip, "filesToZip");

			// 'using' statements guarantee the stream is closed properly which is a big source
			// of problems otherwise.  Its exception safe as well which is great.
			ZipOutputStream s = new ZipOutputStream(File.Create(zipedFile));
			try
			{
				s.SetLevel(compressionLevel); // 0 - store only to 9 - means best compression
				s.Password = password; // set password
				s.SetComment(comment);
				Byte[] buffer = new Byte[4096];

				for (Int32 idx = 0; idx < filesToZip.Length; idx++)
				{
					String file = filesToZip[idx];

					// Using GetFileName makes the result compatible with XP
					// as the resulting path is not absolute.
					ZipEntry entry = new ZipEntry(Path.GetFileName(file));

					// Setup the entry data as required.
					// Crc and size are handled by the library for seakable streams
					// so no need to do them here.
					// Could also use the last write time or similar for the file.
					entry.DateTime = DateTime.Now;
					s.PutNextEntry(entry);

					using (FileStream fs = File.OpenRead(file))
					{
						// Using a fixed size buffer here makes no noticeable difference for output
						// but keeps a lid on memory usage.
						Int32 sourceBytes;

						do
						{
							sourceBytes = fs.Read(buffer, 0, buffer.Length);
							s.Write(buffer, 0, sourceBytes);
						} while (sourceBytes > 0);
					}
				}
			}
			finally
			{
				// Finish/Close arent needed strictly as the using statement does this automatically
				// Finish is important to ensure trailing information for a Zip file is appended.  Without this
				// the created file would be invalid.
				s.Finish();

				// Close is important to wrap things up and unlock the file.
				s.Close();
			}
		}

		#endregion

		#region -- method ZipPackFolder --

		public static void ZipPackFolder(String zipedFile, String folderToZip)
		{
			ZipPackFolder(zipedFile, folderToZip, 6, null, null, null, null);
		}

		public static void ZipPackFolder(String zipedFile, String folderToZip, Int32 compressionLevel)
		{
			ZipPackFolder(zipedFile, folderToZip, compressionLevel, null, null, null, null);
		}

		public static void ZipPackFolder(String zipedFile, String folderToZip, String password)
		{
			ZipPackFolder(zipedFile, folderToZip, 6, password, null, null, null);
		}

		public static void ZipPackFolder(String zipedFile, String folderToZip, Int32 compressionLevel, String password, String comment, String fileFilter, String folderFilter)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipedFile, "zipedFile");
			ValidationHelper.ArgumentNullOrEmpty(folderToZip, "folderToZip");
			folderToZip = PathHelper.DirectoryExists(folderToZip);
			ValidationHelper.ArgumentCondition(folderToZip.IsNullOrWhiteSpace(), "Argument {0}: " + folderToZip + "文件夹不存在!");

			FastZip fz = new FastZip();
			fz.CreateEmptyDirectories = true;
			fz.CompressionLevel = compressionLevel;
			fz.Password = password;
			fz.Comment = comment;
			fz.CreateZip(zipedFile, folderToZip, true, fileFilter, folderFilter);
			fz = null;
		}

		#endregion

		#region -- method ZipExtract --

		public static void ZipExtract(String zipFile, String targetFolder)
		{
			ZipExtract(zipFile, targetFolder, null, null, null, false, false);
		}

		public static void ZipExtract(String zipFile, String targetFolder, String password)
		{
			ZipExtract(zipFile, targetFolder, password, null, null, false, false);
		}

		public static void ZipExtract(String zipFile, String targetFolder, String password,
																	String fileFilter, String folderFilter, Boolean restoreDateTime,
																	Boolean restoreAttributes)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipFile, "zipFile");
			zipFile = FileHelper.FileExists(zipFile);
			ValidationHelper.ArgumentCondition(zipFile.IsNullOrWhiteSpace(), "Argument {0}: " + zipFile + "Zip文件不存在!");
			ValidationHelper.ArgumentNullOrEmpty(targetFolder, "targetFolder");
			Overwrite overwrite = Overwrite.Always;

			FastZip fastZip = new FastZip();
			fastZip.RestoreAttributesOnExtract = restoreAttributes;
			fastZip.ExtractZip(zipFile, targetFolder, overwrite, null, fileFilter, folderFilter, restoreDateTime);
		}

		#endregion

		#region -- BZip2 Compress / Decompress --

		public static void BZip2Compress(String zipedFile, String fileToZip)
		{
			BZip2Compress(zipedFile, fileToZip, 9);
		}

		public static void BZip2Compress(String zipedFile, String fileToZip, Int32 compressionLevel)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipedFile, "zipedFile");
			fileToZip = FileHelper.FileExists(fileToZip);
			ValidationHelper.ArgumentCondition(fileToZip.IsNullOrWhiteSpace(), "fileToZip", "Argument '{0}': 文件不存在!");

			using (Stream input = File.OpenRead(fileToZip), output = File.Create(zipedFile))
			{
				BZip2.Compress(input, output, false, compressionLevel);
			}
		}

		public static void BZip2Decompress(String zipFile, String targetFile)
		{
			ValidationHelper.ArgumentNullOrEmpty(targetFile, "targetFile");
			zipFile = FileHelper.FileExists(zipFile);
			ValidationHelper.ArgumentCondition(zipFile.IsNullOrWhiteSpace(), "zipFile", "Argument '{0}': BZip2文件不存在!");

			using (Stream input = File.OpenRead(zipFile), output = File.Create(targetFile))
			{
				BZip2.Decompress(input, output, false);
			}
		}

		#endregion

		#region -- GZip Compress / Decompress --

		public static void GZipCompress(String zipedFile, String fileToZip)
		{
			GZipCompress(zipedFile, fileToZip, 4096);
		}

		public static void GZipCompress(String zipedFile, String fileToZip, Int32 bufferSize)
		{
			ValidationHelper.ArgumentNullOrEmpty(zipedFile, "zipedFile");
			fileToZip = FileHelper.FileExists(fileToZip);
			ValidationHelper.ArgumentCondition(fileToZip.IsNullOrWhiteSpace(), "fileToZip", "Argument '{0}': 文件不存在!");

			using (Stream input = File.OpenRead(fileToZip), output = File.Create(zipedFile))
			{
				GZip.Compress(input, output, bufferSize, false);
			}
		}

		public static void GZipDecompress(String zipFile, String targetFile)
		{
			GZipDecompress(zipFile, targetFile, 4096);
		}

		public static void GZipDecompress(String zipFile, String targetFile, Int32 bufferSize)
		{
			ValidationHelper.ArgumentNullOrEmpty(targetFile, "targetFile");
			zipFile = FileHelper.FileExists(zipFile);
			ValidationHelper.ArgumentCondition(zipFile.IsNullOrWhiteSpace(), "zipFile", "Argument '{0}': GZip文件不存在!");

			using (Stream input = File.OpenRead(zipFile), output = File.Create(targetFile))
			{
				GZip.Decompress(input, output, bufferSize, false);
			}
		}

		#endregion

		#region -- method CompressDataSet --

		///// <summary>
		///// Compresses specified dataset with GZIP.
		///// </summary>
		///// <param name="ds">DataSet to compress.</param>
		///// <returns>Returns gzipped dataset xml data.</returns>
		//public static Byte[] CompressDataSet(DataSet ds)
		//{
		//	MemoryStream input = new MemoryStream();
		//	ds.WriteXml(input);
		//	MemoryStream output = new MemoryStream();

		//	try
		//	{
		//		using (GZipOutputStream gzipOutput = new GZipOutputStream(output, 4096))
		//		{
		//			StreamUtils.Copy(input, gzipOutput, new Byte[4096]);
		//			return output.ToArray();
		//		}
		//	}
		//	finally
		//	{
		//		input.Close();
		//		output.Close();
		//	}
		//}

		#endregion
	}
}